﻿using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using Csla;
using Csla.Rules;
using Csla.Rules.CommonRules;
using Csla.Server;
using BizControl.DalLinq;
using Csla.Data;
using Csla.Reflection;
using System.Data.Linq;
using BizControl.Resources;
using BizControl.Library.BusinessObjects.People;
using BizControl.Library.Security;

namespace BizControl.Library.BusinessObjects.Marks
{
  [Serializable()]
  public class PersonMarkLinkBO : BusinessBase<PersonMarkLinkBO>
  {
    #region Business Methods

    private static readonly PropertyInfo<MarkInfo> MarkProperty = RegisterProperty<MarkInfo>(c => c.Mark);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors),
      ErrorMessageResourceName = @"PersonMarkLinkMarkRequiredPropertyError")]
    public MarkInfo Mark
    {
      get { return GetProperty(MarkProperty); }
      private set { SetProperty(MarkProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> PersonProperty = RegisterProperty<PersonInfo>(c => c.Person);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"PersonMarkPersonRequiredPropertyError")]
    public PersonInfo Person
    {
      get { return GetProperty(PersonProperty); }
      private set { SetProperty(PersonProperty, value); }
    }

    #region Common properties

    private static readonly PropertyInfo<bool> IsDeletedFieldProperty = RegisterProperty<bool>(c => c.IsDeletedField, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"IsDeletedRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"IsDeletedFieldProperty")]
    public bool IsDeletedField
    {
      get { return GetProperty(IsDeletedFieldProperty); }
      private set { SetProperty(IsDeletedFieldProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> CreatedByProperty = RegisterProperty<PersonInfo>(c => c.CreatedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedByProperty")]
    public PersonInfo CreatedBy
    {
      get { return GetProperty(CreatedByProperty); }
      private set { SetProperty(CreatedByProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> CreatedOnProperty = RegisterProperty<DateTime>(c => c.CreatedOn);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedOnRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedOnProperty")]
    public DateTime CreatedOn
    {
      get { return GetProperty(CreatedOnProperty); }
      private set { SetProperty(CreatedOnProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> LastChangedProperty = RegisterProperty<DateTime>(c => c.LastChanged);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedProperty")]
    public DateTime LastChanged
    {
      get { return GetProperty(LastChangedProperty); }
      private set { SetProperty(LastChangedProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> LastChangedByProperty = RegisterProperty<PersonInfo>(c => c.LastChangedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedByProperty")]
    public PersonInfo LastChangedBy
    {
      get { return GetProperty(LastChangedByProperty); }
      private set { SetProperty(LastChangedByProperty, value); }
    }

    private static readonly PropertyInfo<byte[]> TimeStampProperty = RegisterProperty<byte[]>(c => c.TimeStamp);
    private byte[] TimeStamp
    {
      get { return GetProperty(TimeStampProperty); }
      set { SetProperty(TimeStampProperty, value); }
    }

    #endregion

    internal void LoadData(PersonMark personMarkDB)
    {
      if (personMarkDB == null) return;

      if (Person == null)
        Person = new PersonInfo(personMarkDB.PersonRef);
      if (Mark == null)
        Mark = new MarkInfo(personMarkDB.MarkRef, MarkType.PersonMark);

      // Common properties
      CreatedBy = new PersonInfo(personMarkDB.CreatedByRef);
      LastChangedBy = new PersonInfo(personMarkDB.LastChangedByRef);

      CreatedOn = personMarkDB.CreatedOn;
      LastChanged = personMarkDB.LastChanged;

      IsDeletedField = personMarkDB.IsDeleted;
      TimeStamp = personMarkDB.TimeStamp.ToArray();
    }

    private void LinkMarkWithPerson(PersonBO person, PersonMarkBO mark)
    {
      if (person != null && !person.IsEmpty()) Person = new PersonInfo(person);
      if (mark != null && !mark.IsEmpty()) Mark = new MarkInfo(mark);
    }

    #endregion

    #region Business Rules

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
      BusinessRules.AddRule(new PersonAndMarkCantBeEmpty { PrimaryProperty = MarkProperty });
      BusinessRules.AddRule(new PersonAndMarkCantBeEmpty { PrimaryProperty = PersonProperty });
    }

    private class PersonAndMarkCantBeEmpty : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (PersonMarkLinkBO)context.Target;
        var person = target.ReadProperty(PersonProperty);
        if (person != null && person.IsEmpty())
          context.AddErrorResult(ValidationErrors.PersonMarkPersonRequiredPropertyError);
        var mark = target.ReadProperty(MarkProperty);
        if (mark != null && mark.IsEmpty())
          context.AddErrorResult(ValidationErrors.PersonMarkLinkMarkRequiredPropertyError);
      }
    }

    #endregion

    #region  Data Access

    private void Child_Create(PersonBO person, PersonMarkBO mark)
    {
      var currentUser = SecurityChecks.GetCurrentUser();
      using (BypassPropertyChecks)
      {
        LinkMarkWithPerson(person, mark);
        CreatedBy = new PersonInfo(currentUser);
        LastChangedBy = new PersonInfo(currentUser);
        CreatedOn = DateTime.Now;
      }
      base.Child_Create();
    }

    private void Child_Fetch(PersonMark data, PersonBO person, PersonMarkBO mark)
    {
      using (BypassPropertyChecks)
      {
        LinkMarkWithPerson(person, mark);
        LoadData(data);
      }
    }

    private void Child_Insert()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        Binary lastTimeStamp = null;
        using (BypassPropertyChecks)
        {
          ctx.DataContext.AddPersonMark(
            Mark == null || Mark.IsEmpty() ? (Guid?)null : Mark.Id,
            Person == null || Person.IsEmpty() ? (Guid?)null : Person.Id,
            CreatedBy == null || CreatedBy.IsEmpty() ? (Guid?)null : CreatedBy.Id,
            IsDeletedField,
            CreatedOn,
            LastChanged,
            LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
            ref lastTimeStamp);
          TimeStamp = lastTimeStamp.ToArray();
        }
      }
    }

    private void Child_Update()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        if (IsSelfDirty)
        {
          Binary lastTimeStamp = null;
          LastChangedBy = new PersonInfo(SecurityChecks.GetCurrentUser());
          LastChanged = DateTime.Now;
          using (BypassPropertyChecks)
          {
            ctx.DataContext.UpdatePersonMark(
              Mark == null || Mark.IsEmpty() ? (Guid?)null : Mark.Id,
              Person == null || Person.IsEmpty() ? (Guid?)null : Person.Id,
              IsDeletedField,
              LastChanged,
              LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
              TimeStamp,
              ref lastTimeStamp);
            TimeStamp = lastTimeStamp.ToArray();
          }
        }
      }
    }

    #endregion

    #region  Factory Methods

    internal static PersonMarkLinkBO NewPersonMarkLink(PersonBO person, PersonMarkBO mark)
    {
      return Csla.DataPortal.CreateChild<PersonMarkLinkBO>(person, mark);
    }

    internal static PersonMarkLinkBO GetPersonMarkLink(PersonMark data, PersonBO person)
    {
      return Csla.DataPortal.FetchChild<PersonMarkLinkBO>(data, person, null);
    }

    internal static PersonMarkLinkBO GetPersonMarkLink(PersonMark data, PersonMarkBO mark)
    {
      return Csla.DataPortal.FetchChild<PersonMarkLinkBO>(data, null, mark);
    }

    private PersonMarkLinkBO()
    { /* require use of factory methods */ }

    #endregion
  }
}